package org.codingq92.frame.spring;

import org.codingq92.frame.spring.ann.CusAutowired;
import org.codingq92.frame.spring.ann.CusComponent;
import org.codingq92.frame.spring.config.BeanDefinition;
import org.codingq92.frame.spring.config.BeanPostProcess;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class BeanContext {

    private static final HashMap<String, Object> BEAN_MAP = new HashMap<>();
    private static final HashMap<String, Object> LOADING_BEAN_MAP = new HashMap<>();
    private static final HashMap<String, BeanDefinition> BEAN_DEFINITION_MAP = new HashMap<>();

    /*** bean的前置后置执行方法 */
    private static final List<BeanPostProcess> BEAN_POST_PROCESS_LIST = new ArrayList<>();

    public BeanContext(String packageName) throws Exception {
        init(packageName);
    }

    private void init(String packageName) throws Exception {
        // 获取所有的Bean定义
        getClassListFromPackage(packageName).stream()
                .filter(clazz -> clazz.isAnnotationPresent(CusComponent.class))// 只要被CusComponent注解的类
                .forEach(this::newBeanDefinition); // 创建BeanDefinition
        // 先将bean的前置后置处理(BeanPostProcess)
        BEAN_DEFINITION_MAP.values().stream()
                .filter(clazz -> BeanPostProcess.class.isAssignableFrom(clazz.getBeanType())) // 判断是不是BeanPostProcess的类型
                .map(this::createBean) // 创建bean
                .map(BeanPostProcess.class::cast) // 强转为BeanPostProcess
                .forEach(BEAN_POST_PROCESS_LIST::add); // 缓存
        // 创建所有的bean对象
        BEAN_DEFINITION_MAP.values().forEach(this::createBean);
    }

    /**
     * 拿不到bean的可能 也许是还未加载到~所以 init 方法 分为了俩步
     * 第一步 缓存哪些bean有信息了要开始加载了 第二步 缓存创建好的bean
     *
     * @param name bean name
     */
    public Object getBean(String name) {
        if (null == name) {
            return null;
        }
        // 获取到bean直接返回
        Object bean = BEAN_MAP.get(name);
        if (null != bean) {
            return bean;
        }
        // 未获取到bean则去看是不是等待创建(就是已经组装了bean定义但是未开始create)
        if (BEAN_DEFINITION_MAP.containsKey(name)) {
            // 创建bean并返 回
            return createBean(BEAN_DEFINITION_MAP.get(name));
        }
        return null;
    }

    /**
     * 根据类型获取Bean实例
     *
     * @param byType 要获取的Bean的类型
     * @param <T>    Bean类型的泛型参数
     * @return 返回指定类型的Bean实例，如果找不到则返回null
     */
    public <T> T getBean(Class<T> byType) {
        // 从Bean定义映射中查找第一个与指定类型匹配的Bean名称
        String beanName = BEAN_DEFINITION_MAP.values().stream()
                .filter(obj -> byType.isAssignableFrom(obj.getBeanType()))
                .map(BeanDefinition::getName)
                .findFirst().orElse(null);
        if (null == beanName) {
            return null;
        }
        return (T) getBean(beanName);
    }

    /**
     * 根据指定类型获取所有匹配的Bean实例列表
     *
     * @param byType 要匹配的Bean类型，返回的Bean实例必须是该类型的子类或实现类
     * @param <T>    Bean的类型参数
     * @return 匹配指定类型的所有Bean实例列表
     */
    public <T> List<T> getBeans(Class<T> byType) {
        // 从Bean定义映射中筛选出所有匹配指定类型的Bean，并转换为实例列表
        return BEAN_DEFINITION_MAP.values().stream()
                .filter(obj -> byType.isAssignableFrom(obj.getBeanType()))
                .map(BeanDefinition::getName)
                .map(this::getBean)
                .map(bean -> (T) bean)
                .toList();
    }

    /**
     * 创建Bean实例
     *
     * @param beanDefinition Bean定义信息
     * @return 创建的Bean实例
     */
    protected Object createBean(BeanDefinition beanDefinition) {
        String name = beanDefinition.getName();
        // bean已经创建过了
        if (BEAN_MAP.containsKey(name)) {
            return BEAN_MAP.get(name);
        }
        // 正在创建中的bean
        if (LOADING_BEAN_MAP.containsKey(name)) {
            return LOADING_BEAN_MAP.get(name);
        }
        return doCreateBean(beanDefinition);
    }

    /**
     * 创建Bean实例
     *
     * @param beanDefinition Bean定义信息，包含构造器、属性注入信息等
     * @return 创建的Bean实例对象
     */
    protected Object doCreateBean(BeanDefinition beanDefinition) {
        // 获取默认的构造器
        Constructor<?> constructor = beanDefinition.getConstructor();
        Object bean;
        try {
            // 通过构造器创建对象
            bean = constructor.newInstance();
            // 保存正在创建的bean
            LOADING_BEAN_MAP.put(beanDefinition.getName(), bean);
            // 给对象的属性赋值 @CusAutowired 属性
            doAutowired(bean, beanDefinition);
            // 初始化执行
            bean = initBean(bean, beanDefinition);
            // 移除正在创建的bean 并 缓存bean到容器中
            LOADING_BEAN_MAP.remove(beanDefinition.getName());
            BEAN_MAP.put(beanDefinition.getName(), bean);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return bean;
    }

    private Object initBean(Object bean, BeanDefinition beanDefinition) throws Exception {
        // bean统一的前置处理器
        for (BeanPostProcess beanPostProcess : BEAN_POST_PROCESS_LIST) {
            bean = beanPostProcess.postProcessBeforeInitialization(bean, beanDefinition.getName());
        }
        // 创建对象后，执行@CusPostConstruct注解的方法
        Method method = beanDefinition.getPostConstructMethod();
        if (method != null) {
            method.invoke(bean);
        }
        // bean统一的后置处理器
        for (BeanPostProcess beanPostProcess : BEAN_POST_PROCESS_LIST) {
            bean = beanPostProcess.postProcessAfterInitialization(bean, beanDefinition.getName());
        }
        return bean;
    }

    /**
     * 为bean对象执行自动装配操作
     *
     * @param bean           需要进行自动装配的bean实例
     * @param beanDefinition bean的定义信息，包含需要自动装配的字段列表
     * @throws IllegalAccessException 当无法访问字段时抛出此异常
     */
    private void doAutowired(Object bean, BeanDefinition beanDefinition) throws IllegalAccessException {
        List<Field> autowiredFields = beanDefinition.getAutowiredFields();
        // 遍历所有需要自动装配的字段
        for (Field field : autowiredFields) {
            field.setAccessible(true);// 可访问
            Object autowiredBean = getBean(field.getType());// 获取到CusAutowired的bean=属性
            if (autowiredBean == null && field.isAnnotationPresent(CusAutowired.class)
                    && field.getAnnotation(CusAutowired.class).required()) {
                throw new RuntimeException("容器中找不到匹配的依赖!!!!!");
            }
            field.set(bean, autowiredBean);
        }
    }

    /**
     * 创建新的Bean定义并注册到Bean定义映射中
     *
     * @param aClass 需要创建Bean定义的类对象
     */
    protected void newBeanDefinition(Class<?> aClass) {
        BeanDefinition beanDefinition = new BeanDefinition(aClass);
        // BeanDefinition不可以重复new 解决名字重复的问题
        if (BEAN_DEFINITION_MAP.containsKey(beanDefinition.getName())) {
            throw new RuntimeException("bean name is exist");
        }
        BEAN_DEFINITION_MAP.put(beanDefinition.getName(), beanDefinition);
    }

    /**
     * 从指定包中获取所有类的列表
     *
     * @param packageName 包名，用于查找该包下的所有类文件
     * @return 返回指定包下的所有Class对象列表
     * @throws IOException 当访问包路径出现IO异常时抛出
     */
    protected List<Class<?>> getClassListFromPackage(String packageName) throws IOException {
        List<Class<?>> classList = new ArrayList<>();
        // 获取指定包下的类
        String transPath = packageName.replace(".", File.separator);
        URL resource = BeanContext.class.getClassLoader().getResource(transPath);
        assert resource != null;
        Path path = Path.of(resource.getFile());
        // 递归获取指定包下的所有文件
        Files.walkFileTree(path, new SimpleFileVisitor<>() {
            @Override
            public @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) {
                if (file.toString().endsWith(".class")) {
                    // 获取类名
                    int index = file.toString().indexOf(transPath);
                    String className = file.toString().substring(index, file.toString().length() - ".class".length());
                    className = className.replace(File.separator, ".");
                    try {
                        // 通过反射获取类
                        Class<?> aClass = Class.forName(className);
                        classList.add(aClass);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                }
                return FileVisitResult.CONTINUE;
            }
        });
        return classList;
    }

}
